package com.lcf.framework.utils.oss.minio;

import cn.hutool.core.util.IdUtil;
import com.lcf.framework.exception.SysException;
import com.lcf.framework.utils.FileUtils;
import com.lcf.framework.utils.ImageUtils;
import com.lcf.framework.config.oss.MinioOssConfig;
import com.lcf.framework.utils.oss.IOssUtils;
import io.minio.*;
import io.minio.http.Method;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.TimeUnit;

/**
 * 文件上传
 *
 * @author lcf
 */
@Slf4j
@Component
public class MinioOssUtils implements IOssUtils {
    @Resource
    private MinioOssConfig minioOssConfig;
    private static final int LIMIT_DAYS = 7;
    /**
     * bucket权限-只读
     */
    private static final String READ_ONLY = """
            {
                "Version":"2012-10-17",
                "Statement":[
                    {
                        "Effect":"Deny",
                        "Principal": {
                                        "AWS": "arn:aws:iam::minio:user/minioadmin"
                                    },
                        "Action":[
                            "s3:GetObject"
                        ],
                        "Resource":[
                            "arn:aws:s3:::%s/*"
                        ]
                    }
                ]
            }""";
    /**
     * bucket权限-只写
     */
    private static final String WRITE_ONLY = """
            {
                "Version":"2012-10-17",
                "Statement":[
                    {
                        "Effect":"Deny",
                        "Principal": {
                                        "AWS": "arn:aws:iam::minio:user/minioadmin"
                                    },
                        "Action":[
                            "s3:AbortMultipartUpload",
                            "s3:DeleteObject",
                            "s3:ListMultipartUploadParts",
                            "s3:PutObject"
                        ],
                        "Resource":[
                            "arn:aws:s3:::%s/*"
                        ]
                    }
                ]
            }""";
    /**
     * bucket权限-读写
     */
    private static final String READ_WRITE = """
            {
                "Version":"2012-10-17",
                "Statement":[
                    {
                        "Effect":"Allow",
                        "Principal": {
                                        "AWS": "arn:aws:iam::minio:user/minioadmin"
                                    },
                        "Action":[
                            "s3:DeleteObject",
                            "s3:GetObject",
                            "s3:ListMultipartUploadParts",
                            "s3:PutObject",
                            "s3:AbortMultipartUpload"
                        ],
                        "Resource":[
                            "arn:aws:s3:::%s/*"
                        ]
                    }
                ]
            }""";

    /**
     * 上传图片
     *
     * @param file 图片
     */
    @Override
    public String uploadImg(String bucket, MultipartFile file, boolean isCompress) {
        try {
            return uploadImg(bucket, file.getInputStream(), FileUtils.getFileName(file.getOriginalFilename()), isCompress);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传图片
     *
     * @param path       相对路径
     * @param isCompress 是否压缩
     */
    @Override
    public String uploadImg(String path, InputStream inputStream, boolean isCompress) {
        // 随机文件名
        String fileName = IdUtil.getSnowflake().nextIdStr() + ".jpg";
        try {
            return uploadImg(path, inputStream, fileName, isCompress);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传图片
     *
     * @param fileUrl 图片路径
     */
    @Override
    public String uploadImg(String bucket, String fileUrl, boolean isCompress) {
        try {
            return uploadImg(bucket, new URL(fileUrl).openStream(), FileUtils.getFileName(fileUrl), isCompress);
        } catch (Exception e) {
            throw new SysException("文件不存在");
        }
    }

    /**
     * 更新图片
     *
     * @param inputStream 图片流
     * @param oldName     旧图片名
     */
    @Override
    public String updateImg(String bucket, InputStream inputStream, String oldName, boolean isCompress) {
        delFile(bucket, oldName);
        return uploadImg(bucket, inputStream, oldName, isCompress);
    }

    /**
     * 更新图片
     *
     * @param file    图片
     * @param oldName 旧图片名
     */
    @Override
    public String updateImg(String bucket, MultipartFile file, String oldName, boolean isCompress) {
        delFile(bucket, oldName);
        return uploadImg(bucket, file, isCompress);
    }

    /**
     * 更新图片
     *
     * @param fileUrl 新图片路径
     * @param oldName 旧图片名
     */
    @Override
    public String updateImg(String bucket, String fileUrl, String oldName, boolean isCompress) {
        delFile(bucket, oldName);
        return uploadImg(bucket, fileUrl, isCompress);
    }

    /**
     * 上传文件
     *
     * @param file 文件
     */
    @Override
    public String uploadFile(String bucket, MultipartFile file) {
        try {
            return uploadFile(bucket, file.getInputStream(), FileUtils.getFileName(file.getOriginalFilename()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传文件
     *
     * @param fileUrl 文件路径
     */
    @Override
    public String uploadFile(String bucket, String fileUrl) {
        try {
            return uploadFile(bucket, new URL(fileUrl).openStream(), FileUtils.getFileName(fileUrl));
        } catch (Exception e) {
            throw new SysException("文件不存在");
        }
    }

    /**
     * 更新文件
     *
     * @param inputStream 文件流
     * @param oldName     旧文件名
     */
    @Override
    public String updateFile(String bucket, InputStream inputStream, String oldName) {
        delFile(bucket, oldName);
        return uploadFile(bucket, inputStream, oldName);
    }

    /**
     * 更新文件
     *
     * @param file    文件
     * @param oldName 旧文件名
     */
    @Override
    public String updateFile(String bucket, MultipartFile file, String oldName) {
        delFile(bucket, oldName);
        return uploadFile(bucket, file);
    }

    /**
     * 更新文件
     *
     * @param fileUrl 新文件路径
     * @param oldName 旧文件名
     */
    @Override
    public String updateFile(String bucket, String fileUrl, String oldName) {
        delFile(bucket, oldName);
        return uploadFile(bucket, fileUrl);
    }

    @Override
    public Long getFileSize(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return null;
        }

        String[] names = fileName.split("/");
        StatObjectResponse statObjectResponse = getObjectInfo(names[0], names[1]);
        return statObjectResponse == null ? null : statObjectResponse.size();
    }

    /**
     * 获取文件绝对路径
     *
     * @param fileName 文件名
     */
    @Override
    public String getAbsoluteUrl(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return "";
        }

        String[] names = fileName.split("/");
        try {
            return getClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs
                    .builder()
                    .expiry(LIMIT_DAYS, TimeUnit.DAYS)
                    .bucket(names[0])
                    .object(names[1])
                    .method(Method.GET)
                    .build());
        } catch (Exception e) {
            log.error("获取文件绝对路径失败", e);
            return "";
        }
    }

    /**
     * 获取文件相对路径
     */
    @Override
    public String getRelativeUrl(String url) {
        if (StringUtils.isBlank(url)) {
            return "";
        }
        url = url.substring(url.lastIndexOf("/", url.lastIndexOf("/") - 1) + 1);
        url = url.contains("?") ? url.substring(0, url.indexOf("?")) : url;
        return url;
    }

    /**
     * 上传文件
     *
     * @param inputStream 文件流
     * @param fileName    旧文件名
     */
    private String uploadFile(String bucket, InputStream inputStream, String fileName) {
        try {
            //检查并创建bucket
            createBucket(bucket);
            //随机文件名
            String newFileName = FileUtils.getRandomFileName(fileName);
            //上传
            getClient().putObject(PutObjectArgs.builder()
                    .bucket(bucket)
                    .object(newFileName)
                    .stream(inputStream, inputStream.available(), -1)
                    .build());

            return getAbsoluteUrl(bucket + "/" + newFileName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException ignored) {
                }
            }
        }
    }

    /**
     * 上传图片
     *
     * @param inputStream 文件流
     * @param fileName    旧文件名
     */
    private String uploadImg(String bucket, InputStream inputStream, String fileName, boolean isCompress) {
        try (InputStream stream = isCompress ? ImageUtils.compressImg(inputStream) : inputStream) {
            //检查并创建bucket
            createBucket(bucket);
            //随机文件名
            String newFileName = FileUtils.getRandomFileName(fileName);
            //上传
            getClient().putObject(PutObjectArgs.builder()
                    .bucket(bucket)
                    .object(newFileName)
                    .stream(stream, stream.available(), -1)
                    .build());
            return getAbsoluteUrl(bucket + "/" + newFileName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException ignored) {
                }
            }
        }
    }

    /**
     * 获取文件信息
     */
    public StatObjectResponse getObjectInfo(String bucket, String object) {
        try {
            return getClient().statObject(StatObjectArgs.builder().bucket(bucket).object(object).build());
        } catch (Exception e) {
            log.info("获取文件信息出错: {}, {}, {}", e.getMessage(), bucket, object);
            return null;
        }
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名称
     */
    @Override
    public void delFile(String bucket, String fileName) {
        try {
            getClient().removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucket)
                            .object(fileName)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建桶
     */
    private void createBucket(String bucket) throws Exception {
        MinioClient client = getClient();
        boolean isExist = client.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        if (!isExist) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
            // 设置桶读写权限
            setBucketPolicy(bucket, "read-write");
        }
    }

    /**
     * 更新桶权限策略
     *
     * @param bucket 桶
     * @param policy 权限
     */
    private void setBucketPolicy(String bucket, String policy) throws Exception {
        MinioClient client = getClient();
        String config;
        switch (policy) {
            case "read-only":
                config = String.format(READ_ONLY, bucket);
                client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(config).build());
                break;
            case "write-only":
                config = String.format(WRITE_ONLY, bucket);
                client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(config).build());
                break;
            case "read-write":
                config = String.format(READ_WRITE.replace("minioadmin", minioOssConfig.getAccessKey()), bucket);
                client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(config).build());
                break;
            case "none":
            default:
                break;
        }
    }

    /**
     * 获取客户端
     */
    private MinioClient getClient() {
        return MinioClient.builder()
                .endpoint(minioOssConfig.getEndPoint())
                .credentials(minioOssConfig.getAccessKey(), minioOssConfig.getSecretKey())
                .build();
    }
}
